<template>
  <div class="custom-scan-container">
    <!-- 页面头部 -->
    <div class="page-header">
      <div class="header-content">
        <div class="header-left">
          <h1 class="page-title">
            <el-icon class="title-icon"><DocumentAdd /></el-icon>
            定制扫描任务
          </h1>
          <p class="page-subtitle">创建和管理您的专属DNS安全扫描任务</p>
        </div>
        <div class="header-right">
          <el-button type="primary" size="large" @click="showCreateDialog" class="create-btn">
            <el-icon><Plus /></el-icon>
            创建扫描任务
          </el-button>
        </div>
      </div>
    </div>

    <!-- 统计卡片 -->
    <div class="stats-section">
      <div class="stats-grid">
        <div class="stat-card">
          <div class="stat-icon total-tasks">
            <el-icon><DataAnalysis /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ taskStats.total }}</div>
            <div class="stat-label">总任务数</div>
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-icon running-tasks">
            <el-icon><Loading /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ taskStats.running }}</div>
            <div class="stat-label">运行中</div>
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-icon completed-tasks">
            <el-icon><CircleCheck /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ taskStats.completed }}</div>
            <div class="stat-label">已完成</div>
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-icon failed-tasks">
            <el-icon><CircleClose /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ taskStats.failed }}</div>
            <div class="stat-label">失败</div>
          </div>
        </div>

        <!-- 新增：用户任务配额卡片 -->
        <div class="stat-card quota-card">
          <div class="stat-icon user-quota">
            <el-icon><Lock /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ taskQuota.userRemaining }}</div>
            <div class="stat-label">个人剩余</div>
            <div class="stat-subtitle">{{ taskQuota.userTodayCount }}/{{ taskQuota.maxUserTasks }}</div>
          </div>
        </div>

        <!-- 新增：全局任务配额卡片 -->
        <div class="stat-card quota-card">
          <div class="stat-icon global-quota">
            <el-icon><Connection /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ taskQuota.globalRemaining }}</div>
            <div class="stat-label">全局剩余</div>
            <div class="stat-subtitle">{{ taskQuota.totalTodayCount }}/{{ taskQuota.maxTotalTasks }}</div>
          </div>
        </div>
      </div>
    </div>

    <!-- 任务列表 -->
    <div class="tasks-section">
      <div class="section-header">
        <h2>扫描任务列表</h2>
        <div class="filter-controls">
          <el-select v-model="statusFilter" placeholder="状态筛选" clearable size="default" style="width: 120px;">
            <el-option label="全部" value="" />
            <el-option label="运行中" value="running" />
            <el-option label="已完成" value="completed" />
            <el-option label="失败" value="failed" />
          </el-select>
          <el-input
            v-model="searchKeyword"
            placeholder="搜索任务名称"
            clearable
            size="default"
            style="width: 200px;"
            @input="handleSearch"
          >
            <template #prefix>
              <el-icon><Search /></el-icon>
            </template>
          </el-input>
        </div>
      </div>

      <div class="tasks-grid" v-loading="loading">
        <div 
          v-for="task in filteredTasks" 
          :key="task.taskId" 
          class="task-card"
          :class="{ 'task-running': task.status === 'running' }"
        >
          <div class="task-header">
            <div class="task-title">
              <h3>{{ task.taskName }}</h3>
              <el-tag 
                :type="getStatusType(task.status)" 
                size="small"
                class="status-tag"
              >
                {{ getStatusText(task.status) }}
              </el-tag>
            </div>
                      <div class="task-actions">
            <el-button 
              v-if="task.status === 'completed'" 
              type="success" 
              text 
              @click="exportResults(task)"
              size="small"
            >
              <el-icon><Document /></el-icon>
              导出JSON
            </el-button>
            <el-button 
              type="danger" 
              text 
              @click="deleteTask(task)"
              size="small"
            >
              <el-icon><Delete /></el-icon>
              删除
            </el-button>
          </div>
          </div>

          <div class="task-info">
            <div class="info-row">
              <span class="info-label">扫描类型：</span>
              <div class="scan-types">
                <el-tag v-if="task.scanTypes.includes('dnssec')" size="small" type="info">DNSSEC</el-tag>
                <el-tag v-if="task.scanTypes.includes('dot')" size="small" type="success">DoT</el-tag>
                <el-tag v-if="task.scanTypes.includes('doh')" size="small" type="warning">DoH</el-tag>
              </div>
            </div>
            <div class="info-row">
              <span class="info-label">目标数量：</span>
              <span class="info-value">{{ task.targetCount }} 个</span>
            </div>
            <div class="info-row">
              <span class="info-label">创建时间：</span>
              <span class="info-value">{{ formatDate(task.createdAt) }}</span>
            </div>
            <div class="info-row" v-if="task.estimatedTime">
              <span class="info-label">预计时间：</span>
              <span class="info-value">{{ task.estimatedTime }}</span>
            </div>
          </div>

          <div class="task-progress" v-if="task.status === 'running'">
            <el-progress 
              :percentage="task.progress || 0" 
              :stroke-width="6"
              :show-text="false"
            />
            <span class="progress-text">{{ task.progress || 0 }}%</span>
          </div>
        </div>

        <!-- 空状态 -->
        <div v-if="filteredTasks.length === 0 && !loading" class="empty-state">
          <el-empty description="暂无扫描任务">
            <el-button type="primary" @click="showCreateDialog">创建第一个任务</el-button>
          </el-empty>
        </div>
      </div>
    </div>

    <!-- 创建任务弹窗 -->
    <el-dialog 
      v-model="createDialogVisible" 
      title="创建定制扫描任务" 
      width="800px"
      :before-close="handleCloseCreateDialog"
      class="create-dialog"
      align-center
    >
      <el-form :model="createForm" :rules="createRules" ref="createFormRef" label-position="top" class="create-form">
        <!-- 基本信息 -->
        <div class="form-section">
          <div class="section-header">
            <el-icon class="section-icon"><DocumentAdd /></el-icon>
            <h3>基本信息</h3>
          </div>
          <el-form-item label="任务名称" prop="taskName">
            <el-input 
              v-model="createForm.taskName" 
              placeholder="请输入任务名称，例如：企业DNS安全检测"
              maxlength="50"
              show-word-limit
              size="large"
            />
          </el-form-item>
        </div>

        <!-- 扫描配置 -->
        <div class="form-section">
          <div class="section-header">
            <el-icon class="section-icon"><Setting /></el-icon>
            <h3>扫描配置</h3>
          </div>
          <el-form-item label="选择扫描类型（可多选）" prop="scanTypes">
            <div class="scan-types-container">
              <div 
                class="scan-type-card" 
                :class="{ 'selected': createForm.scanTypes.includes('dnssec') }"
                @click="toggleScanType('dnssec')"
              >
                <div class="type-icon dnssec-icon">
                  <el-icon><Warning /></el-icon>
                </div>
                <div class="type-info">
                  <div class="type-title">DNSSEC</div>
                  <div class="type-subtitle">DNS安全扩展</div>
                </div>
                <div class="check-indicator" v-if="createForm.scanTypes.includes('dnssec')">
                  <el-icon><CircleCheck /></el-icon>
                </div>
              </div>
              
              <div 
                class="scan-type-card" 
                :class="{ 'selected': createForm.scanTypes.includes('dot') }"
                @click="toggleScanType('dot')"
              >
                <div class="type-icon dot-icon">
                  <el-icon><Lock /></el-icon>
                </div>
                <div class="type-info">
                  <div class="type-title">DoT</div>
                  <div class="type-subtitle">DNS over TLS</div>
                </div>
                <div class="check-indicator" v-if="createForm.scanTypes.includes('dot')">
                  <el-icon><CircleCheck /></el-icon>
                </div>
              </div>
              
              <div 
                class="scan-type-card" 
                :class="{ 'selected': createForm.scanTypes.includes('doh') }"
                @click="toggleScanType('doh')"
              >
                <div class="type-icon doh-icon">
                  <el-icon><Connection /></el-icon>
                </div>
                <div class="type-info">
                  <div class="type-title">DoH</div>
                  <div class="type-subtitle">DNS over HTTPS</div>
                </div>
                <div class="check-indicator" v-if="createForm.scanTypes.includes('doh')">
                  <el-icon><CircleCheck /></el-icon>
                </div>
              </div>
            </div>
          </el-form-item>
        </div>

        <!-- 目标配置 -->
        <div class="form-section">
          <div class="section-header">
            <el-icon class="section-icon"><UploadFilled /></el-icon>
            <h3>目标配置</h3>
          </div>
          <el-form-item label="上传目标文件" prop="targetFile">
            <el-upload
              ref="uploadRef"
              class="upload-area"
              drag
              :auto-upload="false"
              :limit="1"
              accept=".txt"
              :on-change="handleFileChange"
              :on-remove="handleFileRemove"
              :file-list="fileList"
            >
              <div class="upload-content">
                <div class="upload-icon">
                  <el-icon><UploadFilled /></el-icon>
                </div>
                <div class="upload-text">
                  <h4>拖拽文件到此处或点击上传</h4>
                  <p>支持 .txt 格式文件，每行一个域名或IP地址</p>
                </div>
              </div>
            </el-upload>
            <div class="upload-tips">
              <div class="tip-item">
                <el-icon><Document /></el-icon>
                <span>支持格式：.txt 文本文件</span>
              </div>
              <div class="tip-item">
                <el-icon><DataAnalysis /></el-icon>
                <span>文件内容：每行一个域名或IP</span>
              </div>
              <div class="tip-item">
                <el-icon><Warning /></el-icon>
                <span>文件大小：不超过 10MB</span>
              </div>
            </div>
          </el-form-item>
        </div>
      </el-form>
      
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="createDialogVisible = false" size="large">
            取消
          </el-button>
          <el-button type="primary" @click="createTask" :loading="createLoading" size="large">
            <el-icon><Plus /></el-icon>
            创建任务
          </el-button>
        </div>
      </template>
    </el-dialog>


  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
  DocumentAdd, Plus, DataAnalysis, Loading, CircleCheck, CircleClose,
  Search, Delete, Warning, Lock, Connection, UploadFilled, Setting, Close, Document
} from '@element-plus/icons-vue'
import customScanApi from '@/api/customScan'
import wsClient from '@/utils/websocket'  // 重新启用WebSocket
import { auth } from '@/utils/auth'

// 响应式数据
const loading = ref(false)
const createDialogVisible = ref(false)
const createLoading = ref(false)
const statusFilter = ref('')
const searchKeyword = ref('')

// 任务统计
const taskStats = reactive({
  total: 0,
  running: 0,
  completed: 0,
  failed: 0
})

// 任务配额信息
const taskQuota = reactive({
  canCreate: true,
  reason: '',
  userTodayCount: 0,
  totalTodayCount: 0,
  maxUserTasks: 5,
  maxTotalTasks: 100,
  userRemaining: 5,
  globalRemaining: 100
})

// 任务列表
const tasks = ref([])

// 创建表单
const createForm = reactive({
  taskName: '',
  scanTypes: [],
  targetFile: null
})

const fileList = ref([])

// 表单验证规则
const createRules = {
  taskName: [
    { required: true, message: '请输入任务名称', trigger: 'blur' },
    { min: 2, max: 50, message: '任务名称长度在 2 到 50 个字符', trigger: 'blur' }
  ],
  scanTypes: [
    { required: true, message: '请选择至少一种扫描类型', trigger: 'change' }
  ],
  targetFile: [
    { required: true, message: '请上传目标文件', trigger: 'change' }
  ]
}

// 表单引用
const createFormRef = ref(null)
const uploadRef = ref(null)

// 计算属性
const filteredTasks = computed(() => {
  let filtered = tasks.value

  // 状态筛选
  if (statusFilter.value) {
    filtered = filtered.filter(task => task.status === statusFilter.value)
  }

  // 关键词搜索
  if (searchKeyword.value) {
    filtered = filtered.filter(task => 
      task.taskName.toLowerCase().includes(searchKeyword.value.toLowerCase())
    )
  }

  return filtered
})

// 生命周期
onMounted(async () => {
  // 加载初始数据
  await Promise.all([
    loadTasks(),
    loadTaskStats(),
    loadTaskQuota()
  ])
  
  // 初始化WebSocket连接（如果失败不影响页面正常使用）
  try {
    await initWebSocket()
  } catch (error) {
    console.warn('WebSocket初始化失败，但不影响页面正常使用:', error)
  }
})

onUnmounted(() => {
  // 清理WebSocket连接
  try {
    cleanupWebSocket()
  } catch (error) {
    console.warn('清理WebSocket时出错:', error)
  }
})

// 方法
const loadTasks = async () => {
  loading.value = true
  try {
    const response = await customScanApi.getTasks({
      status: statusFilter.value,
      keyword: searchKeyword.value
    })
    
    if (response.code === 200) {
      tasks.value = response.data || []
      updateStats()
    } else {
      ElMessage.error(response.message || '获取任务列表失败')
      tasks.value = []
      updateStats()
    }
  } catch (error) {
    console.error('加载任务列表失败:', error)
    
    // 如果是401错误，不显示错误消息，让响应拦截器处理跳转
    if (error.code === 401) {
      console.log('Token过期，响应拦截器将处理跳转')
      return
    }
    
    ElMessage.error(error.message || '网络错误，请检查后端服务是否正常运行')
    tasks.value = []
    updateStats()
  } finally {
    loading.value = false
  }
}

const updateStats = () => {
  taskStats.total = tasks.value.length
  taskStats.running = tasks.value.filter(t => t.status === 'running').length
  taskStats.completed = tasks.value.filter(t => t.status === 'completed').length
  taskStats.failed = tasks.value.filter(t => t.status === 'failed').length
}

const loadTaskStats = async () => {
  try {
    const response = await customScanApi.getTaskStats()
    
    if (response.code === 200) {
      Object.assign(taskStats, response.data)
    } else {
      console.error('获取任务统计失败:', response.message)
    }
  } catch (error) {
    console.error('获取任务统计失败:', error)
    
    // 如果是401错误，不显示错误消息，让响应拦截器处理跳转
    if (error.code === 401) {
      console.log('Token过期，响应拦截器将处理跳转')
      return
    }
  }
}

const loadTaskQuota = async () => {
  try {
    const response = await customScanApi.checkTaskLimit()
    
    if (response.code === 200) {
      const data = response.data
      taskQuota.canCreate = data.canCreate
      taskQuota.reason = data.reason
      taskQuota.userTodayCount = data.userTodayCount
      taskQuota.totalTodayCount = data.totalTodayCount
      taskQuota.maxUserTasks = data.maxUserTasks
      taskQuota.maxTotalTasks = data.maxTotalTasks
      taskQuota.userRemaining = Math.max(0, data.maxUserTasks - data.userTodayCount)
      taskQuota.globalRemaining = Math.max(0, data.maxTotalTasks - data.totalTodayCount)
    } else {
      console.error('获取任务配额失败:', response.message)
    }
  } catch (error) {
    console.error('获取任务配额失败:', error)
    
    // 如果是401错误，不显示错误消息，让响应拦截器处理跳转
    if (error.code === 401) {
      console.log('Token过期，响应拦截器将处理跳转')
      return
    }
  }
}

const showCreateDialog = async () => {
  // 在显示创建对话框前检查配额
  await loadTaskQuota()
  
  if (!taskQuota.canCreate) {
    ElMessage.warning(taskQuota.reason)
    return
  }
  
  createDialogVisible.value = true
}

const handleCloseCreateDialog = () => {
  createFormRef.value?.resetFields()
  createForm.taskName = ''
  createForm.scanTypes = []
  createForm.targetFile = null
  fileList.value = []
  createDialogVisible.value = false
}

const handleFileChange = (file) => {
  if (file.raw) {
    // 验证文件类型
    if (!file.name.endsWith('.txt')) {
      ElMessage.error('只支持 .txt 格式文件')
      return false
    }
    
    // 验证文件大小
    if (file.size > 10 * 1024 * 1024) {
      ElMessage.error('文件大小不能超过 10MB')
      return false
    }
    
    createForm.targetFile = file.raw
    fileList.value = [file]
  }
}

const handleFileRemove = () => {
  createForm.targetFile = null
  fileList.value = []
}

const createTask = async () => {
  try {
    const valid = await createFormRef.value.validate()
    if (!valid) return

    createLoading.value = true

    const response = await customScanApi.createTask({
      taskName: createForm.taskName,
      scanTypes: createForm.scanTypes,
      targetFile: createForm.targetFile
    })

    if (response.code === 200) {
      ElMessage.success(response.message || '任务创建成功，正在开始扫描...')
      handleCloseCreateDialog()
      loadTasks() // 重新加载任务列表
      loadTaskQuota() // 重新加载配额信息
    } else {
      ElMessage.error(response.message || '创建任务失败')
    }

  } catch (error) {
    console.error('创建任务失败:', error)
    
    // 如果是401错误，不显示错误消息，让响应拦截器处理跳转
    if (error.code === 401) {
      console.log('Token过期，响应拦截器将处理跳转')
      return
    }
    
    ElMessage.error(error.message || '创建任务失败，请稍后重试')
  } finally {
    createLoading.value = false
  }
}

const simulateTaskProgress = (task) => {
  const interval = setInterval(async () => {
    try {
      // 调用API获取任务状态
      const response = await customScanApi.getTaskStatus(task.taskId)
      
      if (response.code === 200) {
        const taskIndex = tasks.value.findIndex(t => t.taskId === task.taskId)
        if (taskIndex !== -1) {
          // 更新任务状态和进度
          tasks.value[taskIndex].progress = response.data.progress || 0
          tasks.value[taskIndex].status = response.data.status
          
          if (response.data.status === 'completed') {
            tasks.value[taskIndex].progress = 100
            updateStats()
            clearInterval(interval)
            ElMessage.success(`任务"${task.taskName}"已完成`)
          } else if (response.data.status === 'failed') {
            updateStats()
            clearInterval(interval)
            ElMessage.error(`任务"${task.taskName}"执行失败`)
          }
        } else {
          clearInterval(interval)
        }
      } else {
        // API调用失败，停止轮询
        clearInterval(interval)
        console.error('获取任务状态失败:', response.message)
      }
    } catch (error) {
      console.error('获取任务状态失败:', error)
      
      // 如果是401错误，停止轮询，让响应拦截器处理跳转
      if (error.code === 401) {
        console.log('Token过期，响应拦截器将处理跳转')
        clearInterval(interval)
        return
      }
      
      // 发生其他错误时停止轮询
      clearInterval(interval)
    }
  }, 3000)
}



const exportResults = async (task) => {
  try {
    ElMessage.info('正在导出结果，请稍候...')
    
    const response = await customScanApi.exportTaskResults(task.taskId)
    
    if (response.code === 200) {
      const exportData = response.data
      
      // 创建下载文件
      const blob = new Blob([JSON.stringify(exportData, null, 2)], { 
        type: 'application/json;charset=utf-8' 
      })
      
      // 生成文件名
      const scanType = exportData.meta?.scan_type || 'unknown'
      const taskName = exportData.meta?.task_name || task.taskName
      const timestamp = new Date().toISOString().slice(0, 10)
      const fileName = `${scanType}_results_${taskName}_${timestamp}.json`
      
      // 创建下载链接
      const link = document.createElement('a')
      link.href = URL.createObjectURL(blob)
      link.download = fileName
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      URL.revokeObjectURL(link.href)
      
      ElMessage.success(`导出成功！文件名：${fileName}`)
    } else {
      ElMessage.error(response.message || '导出失败')
    }
  } catch (error) {
    console.error('导出结果失败:', error)
    
    // 如果是401错误，不显示错误消息，让响应拦截器处理跳转
    if (error.code === 401) {
      console.log('Token过期，响应拦截器将处理跳转')
      return
    }
    
    ElMessage.error(error.message || '网络错误，导出失败')
  }
}

const deleteTask = async (task) => {
  try {
    await ElMessageBox.confirm(
      `确定要删除任务"${task.taskName}"吗？此操作不可恢复。`,
      '确认删除',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }
    )

    // 调用真实API删除任务
    const response = await customScanApi.deleteTask(task.taskId)
    
    if (response.code === 200) {
      // API调用成功，从本地列表中移除任务
      const index = tasks.value.findIndex(t => t.taskId === task.taskId)
      if (index !== -1) {
        tasks.value.splice(index, 1)
        updateStats()
        ElMessage.success('任务删除成功')
      }
    } else {
      ElMessage.error(response.message || '删除任务失败')
    }
  } catch (error) {
    if (error !== 'cancel') {
      console.error('删除任务失败:', error)
      
      // 如果是401错误，不显示错误消息，让响应拦截器处理跳转
      if (error.code === 401) {
        console.log('Token过期，响应拦截器将处理跳转')
        return
      }
      
      ElMessage.error(error.message || '删除任务失败，请稍后重试')
    }
    // 如果是用户取消，不显示错误消息
  }
}

const handleSearch = () => {
  // 搜索逻辑已在计算属性中处理
}

const toggleScanType = (type) => {
  const index = createForm.scanTypes.indexOf(type)
  if (index > -1) {
    createForm.scanTypes.splice(index, 1)
  } else {
    createForm.scanTypes.push(type)
  }
}

// 工具函数
const getStatusType = (status) => {
  const typeMap = {
    'running': 'warning',
    'completed': 'success',
    'failed': 'danger'
  }
  return typeMap[status] || 'info'
}

const getStatusText = (status) => {
  const textMap = {
    'running': '运行中',
    'completed': '已完成',
    'failed': '失败'
  }
  return textMap[status] || status
}

const formatDate = (date) => {
  return new Date(date).toLocaleString('zh-CN')
}

// WebSocket功能实现
const initWebSocket = async () => {
  try {
    // 初始化WebSocket连接
    await wsClient.connect()
    
    // 获取当前用户信息
    const userInfo = auth.getUser()
    if (userInfo && userInfo.userId) {
      // 订阅用户特定的配额更新和任务状态更新
      wsClient.subscribeToUserQuota(userInfo.userId)
      wsClient.subscribeToTaskStatus(userInfo.userId)
    }
    
    // 监听全局配额更新
    wsClient.onMessage('globalQuotaUpdate', (data) => {
      if (data.totalTodayCount !== undefined) {
        taskQuota.totalTodayCount = data.totalTodayCount
        taskQuota.globalRemaining = Math.max(0, taskQuota.maxTotalTasks - data.totalTodayCount)
      }
    })
    
    // 监听用户配额更新
    wsClient.onMessage('userQuotaUpdate', (data) => {
      Object.assign(taskQuota, {
        canCreate: data.canCreate,
        reason: data.reason,
        userTodayCount: data.userTodayCount,
        totalTodayCount: data.totalTodayCount,
        maxUserTasks: data.maxUserTasks,
        maxTotalTasks: data.maxTotalTasks,
        userRemaining: data.userRemaining,
        globalRemaining: data.globalRemaining
      })
    })
    
    // 监听任务状态更新
    wsClient.onMessage('taskStatusUpdate', (data) => {
      const { taskId, statusInfo } = data
      
      // 更新任务列表中的任务状态
      const taskIndex = tasks.value.findIndex(t => t.taskId === taskId)
      if (taskIndex !== -1) {
        tasks.value[taskIndex].status = statusInfo.status
        tasks.value[taskIndex].progress = statusInfo.progress || 0
        updateStats()
        
        // 显示任务状态变化通知
        if (statusInfo.status === 'completed') {
          ElMessage.success(`任务"${tasks.value[taskIndex].taskName}"已完成`)
        } else if (statusInfo.status === 'failed') {
          ElMessage.error(`任务"${tasks.value[taskIndex].taskName}"执行失败`)
        }
      }
    })
  } catch (error) {
    console.error('WebSocket连接失败:', error)
    ElMessage.warning('实时功能连接失败，部分功能可能受限')
  }
}

const cleanupWebSocket = () => {
  try {
    // 清理WebSocket连接
    wsClient.disconnect()
    console.log('WebSocket连接已清理')
  } catch (error) {
    console.error('清理WebSocket连接时出错:', error)
  }
}
</script>

<style scoped>
.custom-scan-container {
  min-height: 100vh;
  background: #f8fafc;
  padding: 0;
}

/* 页面头部 */
.page-header {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  padding: 40px 20px;
  margin-bottom: 32px;
}

.header-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: 90vw;
  margin: 0 auto;
}

.header-left {
  flex: 1;
}

.page-title {
  font-size: 32px;
  font-weight: 700;
  margin: 0 0 8px 0;
  display: flex;
  align-items: center;
  gap: 12px;
}

.title-icon {
  font-size: 36px;
}

.page-subtitle {
  font-size: 16px;
  opacity: 0.9;
  margin: 0;
}

.header-right {
  flex-shrink: 0;
}

.create-btn {
  height: 48px;
  padding: 0 24px;
  font-size: 16px;
  border-radius: 24px;
  background: rgba(255, 255, 255, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.3);
  backdrop-filter: blur(10px);
}

.create-btn:hover {
  background: rgba(255, 255, 255, 0.3);
  transform: translateY(-2px);
}

/* 统计卡片 */
.stats-section {
  padding: 0 20px;
  margin-bottom: 32px;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
  max-width: 90vw;
  margin: 0 auto;
}

.stat-card {
  background: white;
  border-radius: 16px;
  padding: 24px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(0, 0, 0, 0.06);
  display: flex;
  align-items: center;
  gap: 20px;
  transition: all 0.3s ease;
}

.stat-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}

.stat-icon {
  width: 56px;
  height: 56px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  color: white;
}

.stat-icon.total-tasks {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.stat-icon.running-tasks {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}

.stat-icon.completed-tasks {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}

.stat-icon.failed-tasks {
  background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
}

.stat-content {
  flex: 1;
}

.stat-number {
  font-size: 28px;
  font-weight: 700;
  color: #1e293b;
  margin-bottom: 4px;
}

.stat-label {
  font-size: 14px;
  color: #64748b;
}

.stat-subtitle {
  font-size: 12px;
  color: #94a3b8;
  margin-top: 2px;
}

/* 任务列表 */
.tasks-section {
  padding: 0 20px 40px;
  max-width: 90vw;
  margin: 0 auto;
}

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 24px;
}

.section-header h2 {
  font-size: 24px;
  font-weight: 600;
  color: #1e293b;
  margin: 0;
}

.filter-controls {
  display: flex;
  gap: 16px;
  align-items: center;
}

.tasks-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 20px;
}

.task-card {
  background: white;
  border-radius: 16px;
  padding: 20px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(0, 0, 0, 0.06);
  transition: all 0.3s ease;
  position: relative;
}

.task-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}

.task-card.task-running {
  border-left: 4px solid #f59e0b;
}

.task-card.task-running::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, #f59e0b, #f97316, #f59e0b);
  background-size: 200% 100%;
  animation: shimmer 2s infinite;
}

@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

.task-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 16px;
}

.task-title {
  flex: 1;
}

.task-title h3 {
  font-size: 16px;
  font-weight: 600;
  color: #1e293b;
  margin: 0 0 6px 0;
}

.status-tag {
  margin-left: 8px;
}

.task-actions {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
}

.task-info {
  margin-bottom: 12px;
}

.info-row {
  display: flex;
  align-items: center;
  margin-bottom: 6px;
}

.info-row:last-child {
  margin-bottom: 0;
}

.info-label {
  font-size: 14px;
  color: #64748b;
  min-width: 80px;
}

.info-value {
  font-size: 14px;
  color: #1e293b;
  font-weight: 500;
}

.scan-types {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.task-progress {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid #f1f5f9;
}

.progress-text {
  font-size: 14px;
  color: #64748b;
  font-weight: 500;
  min-width: 40px;
}

/* 空状态 */
.empty-state {
  grid-column: 1 / -1;
  text-align: center;
  padding: 60px 20px;
}

/* 创建弹窗 */
.create-dialog :deep(.el-dialog) {
  width: 800px;
  max-width: 95vw;
  border-radius: 16px;
  overflow: hidden;
}

.create-dialog :deep(.el-dialog__header) {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  padding: 20px 24px;
  margin: 0;
  position: relative;
}

.create-dialog :deep(.el-dialog__header)::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
}

.create-dialog :deep(.el-dialog__title) {
  color: white;
  font-weight: 600;
  font-size: 20px;
}

.create-dialog :deep(.el-dialog__headerbtn .el-dialog__close) {
  color: white;
  font-size: 20px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  transition: all 0.3s ease;
}

.create-dialog :deep(.el-dialog__headerbtn .el-dialog__close):hover {
  background: rgba(255, 255, 255, 0.2);
  transform: rotate(90deg);
}

.create-dialog :deep(.el-dialog__body) {
  padding: 0;
  background: #fafbfc;
}

.create-form {
  padding: 32px;
}

.form-section {
  margin-bottom: 24px;
  background: white;
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(0, 0, 0, 0.06);
}

.form-section:last-child {
  margin-bottom: 0;
}

.section-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
  padding-bottom: 12px;
  border-bottom: 1px solid #f1f5f9;
}

.section-header h3 {
  font-size: 18px;
  font-weight: 600;
  color: #1e293b;
  margin: 0;
}

.section-icon {
  font-size: 20px;
  color: #667eea;
}

.create-dialog :deep(.el-form-item) {
  margin-bottom: 24px;
}

.create-dialog :deep(.el-form-item__label) {
  font-weight: 600;
  color: #374151;
  font-size: 14px;
  margin-bottom: 8px;
}

.create-dialog :deep(.el-input__wrapper) {
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  border: 1px solid #d1d5db;
  transition: all 0.3s ease;
}

.create-dialog :deep(.el-input__wrapper):hover {
  border-color: #667eea;
}

.create-dialog :deep(.el-input__wrapper.is-focus) {
  border-color: #667eea;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

.scan-types-container {
  display: flex;
  gap: 16px;
  justify-content: center;
  max-width: 650px;
  margin: 0 auto;
}

.scan-type-card {
  width: 180px;
  border: 2px solid #e5e7eb;
  border-radius: 12px;
  padding: 12px 16px;
  transition: all 0.3s ease;
  cursor: pointer;
  position: relative;
  background: white;
  display: flex;
  flex-direction: row;
  align-items: center;
  text-align: left;
  gap: 12px;
  min-height: 55px;
  justify-content: flex-start;
}

.scan-type-card:hover {
  border-color: #c7d2fe;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
}

.scan-type-card.selected {
  border-color: #667eea;
  background: linear-gradient(135deg, #f8faff 0%, #f0f4ff 100%);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}

.scan-type-card.selected::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, #667eea, #764ba2);
  border-radius: 12px 12px 0 0;
}

.type-icon {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  color: white;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  flex-shrink: 0;
}

.type-icon.dnssec-icon {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.type-icon.dot-icon {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}

.type-icon.doh-icon {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}

.type-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.type-title {
  font-size: 14px;
  font-weight: 600;
  color: #1e293b;
  margin: 0;
  line-height: 1.2;
}

.type-subtitle {
  font-size: 11px;
  color: #64748b;
  margin: 0;
  font-weight: 500;
  line-height: 1.2;
}

.check-indicator {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #10b981;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
  animation: checkIn 0.3s ease-out;
}

@keyframes checkIn {
  0% {
    transform: scale(0);
    opacity: 0;
  }
  50% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.upload-area {
  width: 100%;
  margin-bottom: 16px;
  position: relative;
  z-index: 1;
}

.upload-area :deep(.el-upload) {
  width: 100%;
  display: block;
}

.upload-area :deep(.el-upload-dragger) {
  width: 100%;
  height: 100px;
  border: 2px dashed #d1d5db;
  border-radius: 12px;
  background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.upload-area :deep(.el-upload-dragger):hover {
  border-color: #667eea;
  background: linear-gradient(135deg, #f0f4ff 0%, #e0e7ff 100%);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
}

.upload-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px;
  width: 100%;
  height: 100%;
}

.upload-icon {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  color: white;
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}

.upload-text {
  text-align: center;
}

.upload-text h4 {
  font-size: 13px;
  color: #374151;
  font-weight: 600;
  margin: 0 0 2px 0;
}

.upload-text p {
  font-size: 11px;
  color: #6b7280;
  margin: 0;
}

.upload-area :deep(.el-upload-list) {
  margin-top: 12px;
  position: relative;
  z-index: 2;
}

.upload-area :deep(.el-upload-list__item) {
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  padding: 8px 12px;
  margin-bottom: 8px;
  transition: all 0.3s ease;
}

.upload-area :deep(.el-upload-list__item):hover {
  background: #f1f5f9;
  border-color: #cbd5e1;
}

.upload-area :deep(.el-upload-list__item-name) {
  color: #374151;
  font-weight: 500;
  font-size: 13px;
}

.upload-area :deep(.el-upload-list__item-status-label) {
  color: #10b981;
}

.upload-area :deep(.el-icon--close) {
  color: #ef4444;
}

.upload-area :deep(.el-icon--close):hover {
  color: #dc2626;
}

.upload-tips {
  display: flex;
  gap: 24px;
  padding: 12px 16px;
  background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
  border-radius: 8px;
  border-left: 4px solid #0ea5e9;
  flex-wrap: wrap;
  justify-content: center;
}

.tip-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: #0369a1;
  white-space: nowrap;
}

.tip-item .el-icon {
  font-size: 13px;
  color: #0ea5e9;
}

.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 16px;
  padding: 24px 32px;
  background: white;
  border-top: 1px solid #f1f5f9;
  margin: 0;
}

.dialog-footer .el-button {
  height: 40px;
  padding: 0 24px;
  border-radius: 8px;
  font-weight: 500;
  transition: all 0.3s ease;
}

.dialog-footer .el-button--primary {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border: none;
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}

.dialog-footer .el-button--primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
}

/* 中等屏幕响应式设计 */
@media (max-width: 1024px) and (min-width: 769px) {
  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
  }
  
  .tasks-grid {
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
  }
  
  .scan-types-container {
    gap: 12px;
    max-width: 550px;
  }
  
  .scan-type-card {
    width: 160px;
    padding: 10px 14px;
    min-height: 50px;
  }
  
  .type-icon {
    width: 28px;
    height: 28px;
    font-size: 12px;
  }
  
  .type-title {
    font-size: 13px;
  }
  
  .type-subtitle {
    font-size: 10px;
  }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .page-header {
    padding: 24px 20px;
  }
  
  .header-content {
    flex-direction: column;
    gap: 20px;
    text-align: center;
  }
  
  .page-title {
    font-size: 24px;
  }
  
  .stats-section, .tasks-section {
    padding: 0 20px 40px;
  }
  
  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
  }
  
  .section-header {
    flex-direction: column;
    gap: 16px;
    align-items: stretch;
  }
  
  .filter-controls {
    flex-direction: column;
    gap: 12px;
  }
  
  .tasks-grid {
    grid-template-columns: 1fr;
  }
  
  .task-header {
    flex-direction: column;
    gap: 12px;
  }
  
  .task-actions {
    align-self: flex-start;
  }
  
  .summary-stats {
    flex-direction: column;
    gap: 12px;
  }
  
  .scan-types-container {
    flex-direction: column;
    gap: 12px;
  }
  
  .scan-type-card {
    flex-direction: row;
    padding: 16px;
    min-height: auto;
    text-align: left;
    gap: 12px;
    justify-content: flex-start;
  }
  
  .type-icon {
    flex-shrink: 0;
  }
  
  .create-dialog :deep(.el-dialog) {
    width: 95vw !important;
    max-width: 500px;
  }
  
  .create-dialog :deep(.el-dialog__body) {
    padding: 24px;
  }
  
  .create-dialog :deep(.el-form-item) {
    margin-bottom: 24px;
  }
  
  .upload-area :deep(.el-upload-dragger) {
    height: 100px;
  }
  
  .upload-content {
    gap: 6px;
    padding: 12px;
  }
  
  .upload-icon {
    width: 32px;
    height: 32px;
    font-size: 16px;
  }
  
  .upload-text h4 {
    font-size: 13px;
  }
  
  .upload-text p {
    font-size: 11px;
  }
  
  .upload-tips {
    flex-direction: column;
    gap: 8px;
    padding: 12px;
  }
  
  .tip-item {
    font-size: 12px;
  }
  
  .dialog-footer {
    padding: 20px 24px;
  }
  
  .dialog-footer .el-button {
    height: 36px;
    padding: 0 20px;
    font-size: 14px;
  }
}

/* 配额图标样式 */
.user-quota {
  background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
}

.global-quota {
  background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%);
}

.quota-card .stat-number {
  color: #1e293b;
}

.quota-card .stat-subtitle {
  font-weight: 600;
}
</style>
